package org.apache.lucene.util;

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
 * Base class for Attributes that can be added to a 
 * {@link org.apache.lucene.util.AttributeSource}.
 * <p>
 * Attributes are used to add data in a dynamic, yet type-safe way to a source
 * of usually streamed objects, e. g. a {@link org.apache.lucene.analysis.TokenStream}.
 */
public abstract class AttributeImpl implements Cloneable, Serializable, Attribute {  
  /**
   * Clears the values in this AttributeImpl and resets it to its 
   * default value. If this implementation implements more than one Attribute interface
   * it clears all.
   */
  public abstract void clear();
  
  /**
   * The default implementation of this method accesses all declared
   * fields of this object and prints the values in the following syntax:
   * 
   * <pre>
   *   public String toString() {
   *     return "start=" + startOffset + ",end=" + endOffset;
   *   }
   * </pre>
   * 
   * This method may be overridden by subclasses.
   */
  @Override
  public String toString() {
    StringBuilder buffer = new StringBuilder();
    Class clazz = this.getClass();
    Field[] fields = clazz.getDeclaredFields();
    try {
      for (int i = 0; i < fields.length; i++) {
        Field f = fields[i];
        if (Modifier.isStatic(f.getModifiers())) continue;
        f.setAccessible(true);
        Object value = f.get(this);
        if (buffer.length()>0) {
          buffer.append(',');
        }
        if (value == null) {
          buffer.append(f.getName() + "=null");
        } else {
          buffer.append(f.getName() + "=" + value);
        }
      }
    } catch (IllegalAccessException e) {
      // this should never happen, because we're just accessing fields
      // from 'this'
      throw new RuntimeException(e);
    }
    
    return buffer.toString();
  }
  
  /**
   * Subclasses must implement this method and should compute
   * a hashCode similar to this:
   * <pre>
   *   public int hashCode() {
   *     int code = startOffset;
   *     code = code * 31 + endOffset;
   *     return code;
   *   }
   * </pre> 
   * 
   * see also {@link #equals(Object)}
   */
  @Override
  public abstract int hashCode();
  
  /**
   * All values used for computation of {@link #hashCode()} 
   * should be checked here for equality.
   * 
   * see also {@link Object#equals(Object)}
   */
  @Override
  public abstract boolean equals(Object other);
  
  /**
   * Copies the values from this Attribute into the passed-in
   * target attribute. The target implementation must support all the
   * Attributes this implementation supports.
   */
  public abstract void copyTo(AttributeImpl target);
    
  /**
   * Shallow clone. Subclasses must override this if they 
   * need to clone any members deeply,
   */
  @Override
  public Object clone() {
    Object clone = null;
    try {
      clone = super.clone();
    } catch (CloneNotSupportedException e) {
      throw new RuntimeException(e);  // shouldn't happen
    }
    return clone;
  }
}
